using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Lidgren.Network;
using ObjectSync.Interfaces;
using ObjectSync.Util;

namespace ObjectSync
{
    public class ServerCommunicator: ICommunicator
    {
		#region ICommunicator implementation

		public void Send (NetBuffer sendBuffer)
		{
			NetOutgoingMessage msg = Server.CreateMessage();
			msg.Write(sendBuffer.ReadBytes(sendBuffer.LengthBytes));
			
			Send(msg);
		}

		public NetOutgoingMessage GetOutMenssage ()
		{
			return Server.CreateMessage();
		}

		#endregion

		private NetPeerConfiguration Config;
        //private NetConfiguration Config;
        private NetServer Server;
        //private NetBuffer Buffer;

		//private NetIncomingMessage Msg;

        private Thread reader;

        /// <summary>
        /// WARNING only change this variable with the locker locked. It is accessed in more then one thread
        /// </summary>
        private List<Message>  messages = new List<Message>();
        private object locker = new object();
        private bool keepReaderRunning = true;
        private int port = 1717;

        private NetDeliveryMethod defaultDeliverMethod;

		public NetDeliveryMethod DefaultDeliverMethod
        {
			get { return defaultDeliverMethod; }
			set { defaultDeliverMethod = value; }
        }

		private comStatus status = comStatus.diconected;
		
		public comStatus Status {
			get {
				return status;
			}
		}

		public comStatus getStatus () {return Status;}

        public ServerCommunicator(int port)
        {
            this.port = port;
            Initiate();
        }

        public void Initiate()
        {
            // create a configuration for the server
            Config = new NetPeerConfiguration("ObjectSync");
			Config.MaximumConnections = 256;
            Config.Port = port;
			Config.AcceptIncomingConnections = true;

			Config.EnableMessageType(NetIncomingMessageType.DiscoveryRequest);
			Config.DisableMessageType(NetIncomingMessageType.ConnectionApproval);


            // create server and start listening for connections
            Server = new NetServer(Config);
	
            Server.Start();

            //Set the defaultNetChannel
            DefaultDeliverMethod = NetDeliveryMethod.ReliableOrdered;

            // create a buffer to read data into
            //Buffer = Server.CreateBuffer();

            // create the thread reader
            

			ThreadStart job = new ThreadStart(Reader);
            reader = new Thread(job);
            reader.Start();

			//Reader();
        }


        public void Send(NetOutgoingMessage sendBuffer)
        {
            Send(sendBuffer, DefaultDeliverMethod);
        }

		public void Write(string message)
        {
            var enc = new UnicodeEncoding();

            byte[] mbytes = enc.GetBytes(message);

            Write(mbytes);
        }

		public void Write(byte[] message)
        {
			NetOutgoingMessage msg = Server.CreateMessage();
			msg.Write (message);
            Send(msg);
        }

		public void Send(NetOutgoingMessage sendBuffer, NetDeliveryMethod deliverMethod)
        {
            // send to everyone, including sender
            //NetBuffer serverBuffer = Server.CreateBuffer();
            
			Server.SendToAll(sendBuffer, deliverMethod);

        }


        public Message[] Read()
        {
            lock(locker)
            {
                Message[] answer = messages.ToArray();
                messages.Clear();
                return answer;
            }
        }

        private void Reader()
        {

            while (keepReaderRunning)
            {
				NetIncomingMessage message;

				while ((message = Server.ReadMessage()) != null)
				{
					switch (message.MessageType)
					{

					case NetIncomingMessageType.DiscoveryRequest:
						NetOutgoingMessage response = Server.CreateMessage();
						response.Write("Welcome Client");
						Server.SendDiscoveryResponse(response, message.SenderEndPoint);
						break;
					case NetIncomingMessageType.ConnectionApproval:
						message.SenderConnection.Approve();
						break;
					case NetIncomingMessageType.ErrorMessage:
						Console.WriteLine(message.ReadString());
						break;
					case NetIncomingMessageType.StatusChanged:
						Console.WriteLine(String.Format("New status: {0} ({1})", Server.Status, message.ReadString()));
						break;
					case NetIncomingMessageType.Data:
						// A client sent this data!
						UInt16 msgType = message.ReadUInt16();
						
						//Let's see if it's a SYNC
						if (msgType == (UInt16)MessageType.SYNC)
						{
							Syncronization.DecodeMessageAndSync(message, true);
							//SyncManager.Instance.Syncronize();
						}
						else if (msgType == (UInt16)MessageType.Exec)
						{
							NetworkManager.Instance.Communicator.Send(RemoteManager.ExecuteAndEncode(message, Server.CreateMessage() ));
						}
						else 
						{
							//If not let's keep the message talking
							Message m = new Message();
							m.sender = message.SenderConnection;
							m.type = message.MessageType;
							m.msg = message.ReadBytes(message.LengthBytes);

							lock (locker) messages.Add(m);
						}
						break;
					default:
						Console.WriteLine("Unhandled type: " + message.MessageType);
						break;
					}
					Server.Recycle(message);
				}

                Thread.Sleep(10);
            }

        }
    }
}
